using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using slcl.Packets;
using SimpleMessageQueue;

namespace slcl
{
    public class Simulator
    {

        public enum Commands
        {
            Connect,
            Disconnect
        }

        public enum Events
        {
            Connected,
            Disconnected,
            Log
        }

        /// <summary>
        /// this exists only if we are actually connected to this sim.
        /// </summary>
        public SimulatorConnection SimLink;

        /// <summary>
        /// parcels within this sim.
        /// </summary>
        public Dictionary<string, Parcel> Parcels;

        /// <summary>
        /// what grid this sim belongs to.
        /// </summary>
        public Grid Grid;

        /// <summary>
        /// our unique ID within this grid.
        /// </summary>
        public LLUUID ID = LLUUID.Zero;

        /// <summary>A ulong that's an amalgamation of X&Y coordinates
        /// of the sim within the grid.</summary>
        public ulong Handle;

        /// <summary>Obvious. Our name silly!</summary>
        public string Name = String.Empty;

        // moved these from Grid since each sim can have a distinctly different phase
        // , position, etc based on local settings.
        /// <summary>
        /// phase of hte sun.
        /// </summary>
        public float SunPhase { get { return sunPhase; } }
        /// <summary>Current direction of the sun</summary>
        public LLVector3 SunDirection { get { return sunDirection; } }
        /// <summary>Current angular velocity of the sun</summary>
        public LLVector3 SunAngVelocity { get { return sunAngVelocity; } }
        public float sunPhase;
        /// <summary>Current direction of the sun</summary>
        public LLVector3 sunDirection;
        /// <summary>Current angular velocity of the sun</summary>
        public LLVector3 sunAngVelocity;



        /// <summary>*blink* huh?</summary>
        public byte[] ParcelOverlay = new byte[4096];
        /// <summary></summary>
        public int ParcelOverlaysReceived;
        /// <summary></summary>
        public float TerrainHeightRange00;
        /// <summary></summary>
        public float TerrainHeightRange01;
        /// <summary></summary>
        public float TerrainHeightRange10;
        /// <summary></summary>
        public float TerrainHeightRange11;
        /// <summary></summary>
        public float TerrainStartHeight00;
        /// <summary></summary>
        public float TerrainStartHeight01;
        /// <summary></summary>
        public float TerrainStartHeight10;
        /// <summary></summary>
        public float TerrainStartHeight11;

        /// <summary>Elevation where the water ends and air begins.</summary>
        public float WaterHeight;

        /// <summary>ID of the avatar that owns this sim (can it be a group ID?)</summary>
        public LLUUID SimOwner = LLUUID.Zero;

        /// <summary></summary>
        public LLUUID TerrainBase0 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainBase1 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainBase2 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainBase3 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainDetail0 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainDetail1 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainDetail2 = LLUUID.Zero;
        /// <summary></summary>
        public LLUUID TerrainDetail3 = LLUUID.Zero;

        /// <summary></summary>
        public bool IsEstateManager;

        /// <summary></summary>
     // FIXME   public EstateTools Estate;

        /// <summary>Current time dilation of this simulator</summary>
        public float Dilation;

        #region Grid Region info.

        /// <summary>Sim X position on World Map</summary>
        public int X;
        /// <summary>Sim Y position on World Map</summary>
        public int Y;
        /// <summary>Sim Name (NOTE: In lowercase!)</summary>
        public byte Access;
        /// <summary>Various flags for the region (presumably things like PG/Mature)</summary>
        public uint RegionFlags;
        /// <summary>Sim's defined Water Height</summary>
        public byte Agents;
        /// <summary>UUID of the World Map image</summary>
        public LLUUID MapImageID;
        /// <summary>Unique identifier for this region, a combination of the X 
        /// and Y position</summary>
        public ulong RegionHandle;
        
        #endregion
        internal List<LLVector3> avatarPositions;

        /// <summary>
        /// Constructor seems bare. Missing new calls maybe?
        /// </summary>
        public Simulator(Grid g)
        {
            Init(g);
        }
        public Simulator(Grid g, IPAddress ip, ushort port)
        {
            Init(g);
            SimLink = new SimulatorConnection(g, this, ip, port);
        }

        public Simulator(Grid g, IPEndPoint ep)
        {
            Init(g);
            SimLink = new SimulatorConnection(g, this, ep);
        }

        void Init(Grid g)
        {
            Grid = g;
            avatarPositions = new List<LLVector3>();
            Parcels = new Dictionary<string, Parcel>();
        }

        /// <summary>
        /// Anyone got a link here?
        /// </summary>
        /// <returns></returns>
        public bool IsConnected()
        {
            return SimLink != null && SimLink.Connected;
        }

        /// <summary>
        /// waits for the conencted event to be set.
        /// </summary>
        /// <param name="timeout">timeout in ms</param>
        public bool WaitForConnection(int timeout)
        {
            if (null == SimLink)
                return false;
            bool trash = false;
            SimLink.ConnectedEvent.WaitOne(timeout, trash);
            return SimLink.Connected;
        }

        /// <summary>
        /// Direct call to connect to the physical sim server.
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="setDefault"></param>
        /// <param name="seedcaps"></param>
        /// <returns></returns>
        public bool Connect(IPAddress ip, ushort port, string seedcaps)
        {
            if (null == SimLink)
            {
                SimLink = new SimulatorConnection(Grid, this, ip, port);
            }
            return Connect();
        }

        public bool Connect()
        {
            if (null == SimLink)
            {
                Log("No IP information. can't conenct.", LogLevel.Warning);
                return false;
            }
            if (!SimLink.Connected)
            {
                return SimLink.Connect();
            }
            return false;
        }

        public void RemoveConnection()
        {
            // removes our SimLink so that it's garbage collected.
            Log("Link removed for " + ToString(), LogLevel.Debug);
            SimLink = null;
        }

        public void Log(string msg, LogLevel lvl)
        {
            Grid.Log("Sim: " + ToString() + ": " + msg, lvl);
        }


        /// <summary>
        /// direct disconnect method.
        /// </summary>
        /// <returns></returns>
        public bool Disconnect()
        {
            if (null == SimLink || !SimLink.Connected)
                return false;
            Log("Disconnecting from sim " + this.ToString(), LogLevel.Info);
            return SimLink.Disconnect();
        }

        /// <summary>
        /// uses the Simulator Link object to ask the sim for it's info.
        /// </summary>
        void FetchSimInformation()
        {
            // FIXME: implement
        }

        /// <summary>
        /// convenience handle for sending a packet to a specific sim.
        /// </summary>
        /// <param name="p"></param>
        public void SendPacket(Packet p)
        {
            if (!IsConnected())
            {
                Grid.Log("Attempt to send packet on disconnected sim " + this.ToString(), LogLevel.Warning);
                return;
            }
            SimLink.SendPacket(p);
        }

        public override string ToString()
        {
            string ret = Name;
            if (SimLink != null)
                ret += " " + SimLink.ToString();
            return ret;
        }
    }
}
